using FileManager.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace FileManager.Services
{
    public class ScanProgressEventArgs : EventArgs
    {
        public double ProgressPercentage { get; set; }
        public int ProcessedFiles { get; set; }
        public int TotalFiles { get; set; }
        public string CurrentFile { get; set; }
    }

    public class ScanCompletedEventArgs : EventArgs
    {
        public bool Cancelled { get; set; }
        public string Message { get; set; }
        public int TotalFilesScanned { get; set; }
        public int NewFilesAdded { get; set; }
        public int UpdatedFiles { get; set; }
    }

    public class FileScanner
    {
        private readonly AppDbContext _dbContext;
        private CancellationTokenSource _cancellationTokenSource;
        private int _processedFiles;
        private int _totalFiles;
        private int _newFilesAdded;
        private int _updatedFiles;
        private readonly object _lockObject = new object();
        private readonly Regex _wechatFilePattern = new Regex(@"(wxid_|gh_|filehelper|medianote).*", RegexOptions.Compiled);

        public event EventHandler<ScanProgressEventArgs> ProgressChanged;
        public event EventHandler<ScanCompletedEventArgs> ScanCompleted;

        public FileScanner(AppDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public async Task StartScanAsync(List<string> directories)
        {
            _cancellationTokenSource = new CancellationTokenSource();
            _processedFiles = 0;
            _totalFiles = 0;
            _newFilesAdded = 0;
            _updatedFiles = 0;

            try
            {
                // 计算总文件数
                await Task.Run(() =>
                {
                    foreach (var directory in directories)
                    {
                        if (Directory.Exists(directory))
                        {
                            _totalFiles += CountFiles(directory);
                        }
                    }
                });

                // 并行处理每个目录
                var tasks = directories.Select(dir => ScanDirectoryAsync(dir, _cancellationTokenSource.Token)).ToList();
                await Task.WhenAll(tasks);

                // 扫描完成
                OnScanCompleted(false, $"扫描完成。共扫描 {_processedFiles} 个文件，新增 {_newFilesAdded} 个，更新 {_updatedFiles} 个。");
            }
            catch (OperationCanceledException)
            {
                OnScanCompleted(true, "扫描已取消。");
            }
            catch (Exception ex)
            {
                OnScanCompleted(false, $"扫描出错: {ex.Message}");
            }
        }

        public void CancelScan()
        {
            _cancellationTokenSource?.Cancel();
        }

        private int CountFiles(string directory)
        {
            int count = 0;
            try
            {
                count += Directory.GetFiles(directory).Length;
                foreach (var subDir in Directory.GetDirectories(directory))
                {
                    count += CountFiles(subDir);
                }
            }
            catch (Exception)
            {
                // 忽略访问被拒绝等异常
            }
            return count;
        }

        private async Task ScanDirectoryAsync(string directory, CancellationToken cancellationToken)
        {
            if (!Directory.Exists(directory))
                return;

            try
            {
                // 处理当前目录中的文件
                var files = Directory.GetFiles(directory);
                foreach (var filePath in files)
                {
                    if (cancellationToken.IsCancellationRequested)
                        throw new OperationCanceledException();

                    await ProcessFileAsync(filePath);

                    lock (_lockObject)
                    {
                        _processedFiles++;
                        OnProgressChanged(filePath);
                    }
                }

                // 递归处理子目录
                var subDirectories = Directory.GetDirectories(directory);
                var tasks = subDirectories.Select(subDir => ScanDirectoryAsync(subDir, cancellationToken));
                await Task.WhenAll(tasks);
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception)
            {
                // 忽略访问被拒绝等异常
            }
        }

        private async Task ProcessFileAsync(string filePath)
        {
            try
            {
                var fileInfo = new FileInfo(filePath);
                if (!fileInfo.Exists || fileInfo.Length == 0)
                    return;

                // 检查文件是否已在数据库中
                var existingFile = await _dbContext.Files.FirstOrDefaultAsync(f => f.FilePath == filePath);

                if (existingFile == null)
                {
                    // 新文件，添加到数据库
                    var fileItem = new FileItem
                    {
                        FilePath = filePath,
                        FileName = Path.GetFileName(filePath),
                        FileSize = fileInfo.Length,
                        FileType = GetFileType(fileInfo.Extension),
                        Extension = fileInfo.Extension.ToLowerInvariant(),
                        CreateTime = fileInfo.CreationTime,
                        LastAccessTime = fileInfo.LastAccessTime,
                        IsEncrypted = fileInfo.Extension.ToLowerInvariant() == ".dat",
                        HashValue = await CalculateFileHashAsync(filePath, fileInfo.Length),
                        IsImportant = false,
                        Remark = "",
                        CreateIndexTime = DateTime.Now
                    };

                    _dbContext.Files.Add(fileItem);
                    await _dbContext.SaveChangesAsync();

                    // 尝试关联聊天对象
                    await TryAssociateChatObjectAsync(fileItem);

                    lock (_lockObject)
                    {
                        _newFilesAdded++;
                    }
                }
                else if (fileInfo.LastWriteTime > existingFile.LastAccessTime || fileInfo.Length != existingFile.FileSize)
                {
                    // 文件已更新（时间或大小变化），更新数据库记录
                    existingFile.FileSize = fileInfo.Length;
                    existingFile.LastAccessTime = fileInfo.LastAccessTime;
                    existingFile.HashValue = await CalculateFileHashAsync(filePath, fileInfo.Length);

                    await _dbContext.SaveChangesAsync();

                    lock (_lockObject)
                    {
                        _updatedFiles++;
                    }
                }
            }
            catch (Exception ex)
            {
                // 记录错误但继续处理其他文件
                Console.WriteLine($"处理文件 {filePath} 时出错: {ex.Message}");
            }
        }

        private string GetFileType(string extension)
        {
            extension = extension.ToLowerInvariant();

            // 图片类型
            if (new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".dat" }.Contains(extension))
                return "图片";

            // 视频类型
            if (new[] { ".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv" }.Contains(extension))
                return "视频";

            // 文档类型
            if (new[] { ".doc", ".docx", ".pdf", ".xls", ".xlsx", ".ppt", ".pptx", ".txt" }.Contains(extension))
                return "文档";

            // 其他类型
            return "其他";
        }

        private async Task<string> CalculateFileHashAsync(string filePath, long fileSize)
        {
            // 对于大文件，只计算部分内容的哈希值
            if (fileSize > 10 * 1024 * 1024) // 10MB
            {
                return await CalculatePartialHashAsync(filePath);
            }
            else
            {
                return await CalculateFullHashAsync(filePath);
            }
        }

        private async Task<string> CalculateFullHashAsync(string filePath)
        {
            using (var md5 = MD5.Create())
            using (var stream = File.OpenRead(filePath))
            {
                byte[] hash = await md5.ComputeHashAsync(stream);
                return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
            }
        }

        private async Task<string> CalculatePartialHashAsync(string filePath)
        {
            const int chunkSize = 4 * 1024 * 1024; // 4MB
            try
            {
                using (var md5 = MD5.Create())
                using (var stream = File.OpenRead(filePath))
                {
                    // 读取文件开头
                    byte[] buffer = new byte[chunkSize];
                    int bytesRead = await stream.ReadAsync(buffer, 0, chunkSize);
                    md5.TransformBlock(buffer, 0, bytesRead, buffer, 0);

                    // 如果文件足够大，读取文件中间部分
                    if (stream.Length > chunkSize * 2)
                    {
                        stream.Position = stream.Length / 2;
                        bytesRead = await stream.ReadAsync(buffer, 0, chunkSize);
                        md5.TransformBlock(buffer, 0, bytesRead, buffer, 0);
                    }

                    // 读取文件结尾
                    if (stream.Length > chunkSize)
                    {
                        stream.Position = Math.Max(0, stream.Length - chunkSize);
                        bytesRead = await stream.ReadAsync(buffer, 0, chunkSize);
                        md5.TransformFinalBlock(buffer, 0, bytesRead);
                    }
                    else
                    {
                        md5.TransformFinalBlock(new byte[0], 0, 0);
                    }

                    return BitConverter.ToString(md5.Hash).Replace("-", "").ToLowerInvariant();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"计算文件 {filePath} 哈希值时出错: {ex.Message}");
                // 返回一个默认值，表示哈希计算失败
                return "hash_error";
            }
        }

        private async Task TryAssociateChatObjectAsync(FileItem fileItem)
        {
            try
            {
                // 尝试从文件路径中提取微信ID
                var match = _wechatFilePattern.Match(fileItem.FilePath);
                if (match.Success)
                {
                    string wechatId = match.Value;
                    
                    // 检查聊天对象是否已存在
                    var chatObject = await _dbContext.ChatObjects.FirstOrDefaultAsync(c => c.WeChatId == wechatId);
                    
                    if (chatObject == null)
                    {
                        // 创建新的聊天对象
                        chatObject = new ChatObject
                        {
                            Name = wechatId,
                            WeChatId = wechatId,
                            IsGroup = wechatId.StartsWith("gh_") || wechatId.Contains("@chatroom")
                        };
                        
                        _dbContext.ChatObjects.Add(chatObject);
                        await _dbContext.SaveChangesAsync();
                    }
                    
                    // 创建关联
                    var relation = new FileChatRelation
                    {
                        FileId = fileItem.Id,
                        ChatObjectId = chatObject.Id
                    };
                    
                    _dbContext.FileChatRelations.Add(relation);
                    await _dbContext.SaveChangesAsync();
                }
            }
            catch (Exception)
            {
                // 忽略关联错误
            }
        }

        private void OnProgressChanged(string currentFile)
        {
            ProgressChanged?.Invoke(this, new ScanProgressEventArgs
            {
                ProgressPercentage = _totalFiles > 0 ? (_processedFiles * 100.0 / _totalFiles) : 0,
                ProcessedFiles = _processedFiles,
                TotalFiles = _totalFiles,
                CurrentFile = Path.GetFileName(currentFile)
            });
        }

        private void OnScanCompleted(bool cancelled, string message)
        {
            ScanCompleted?.Invoke(this, new ScanCompletedEventArgs
            {
                Cancelled = cancelled,
                Message = message,
                TotalFilesScanned = _processedFiles,
                NewFilesAdded = _newFilesAdded,
                UpdatedFiles = _updatedFiles
            });
        }
    }
}